package com.code.bz.configs.bean;

import org.apache.http.NoHttpResponseException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;

/**
 * RestTemplate配置类
 * RestTemplate使用教程
 * <a href="http://www.jianshu.com/p/c9644755dd5e">http://www.jianshu.com/p/c9644755dd5e</a>
 *
 * @author xiaoyaowang
 */
@Configuration
public class RestTemplateConfig {

    @Value("${rest.template.retry-count}")
    private int retryCount;

    @Value("${rest.template.socket-time-out}")
    private int socketTimeOut;

    @Value("${rest.template.connect-time-out}")
    private int connectTimeOut;

    @Value("${rest.template.connect-request-timeout}")
    private int connectRequestTimeout;

    @Value("${rest.template.max-total}")
    private int maxTotal;

    @Value("${rest.template.max-per-route}")
    private int maxPerRoute;

    @Bean(name = "restTemplate")
    public RestTemplate getRestTemplate(RestTemplateBuilder restTemplateBuilder) throws Exception {
        HttpClient httpClient = getHttpClient();

        RestTemplate restTemplate = restTemplateBuilder.build();
        restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
        return restTemplate;
    }

    public HttpClient getHttpClient() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
        SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (chain, authType) -> true).build();

        //TODO 这些超时、最大连接数等参数最好配置到不同的环境的相关的yml文件中
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeOut).setConnectTimeout(connectTimeOut).setConnectionRequestTimeout(connectRequestTimeout).setRedirectsEnabled(false).build();

        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", PlainConnectionSocketFactory.INSTANCE).register("https", new SSLConnectionSocketFactory(sslContext)).build();

        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        // 将最大连接数增加到200
        connectionManager.setMaxTotal(maxTotal);
        // 将每个路由基础的连接增加到40
        connectionManager.setDefaultMaxPerRoute(maxPerRoute);

        HttpRequestRetryHandler requestRetryHandler = (exception, executionCount, context) ->
        {
            //只有异常为NoHttpResponseException 且重试不超过3次时才重试
            if (executionCount <= retryCount && exception instanceof NoHttpResponseException) {
                return true;
            }
            return false;
        };

        HttpClientBuilder clientBuilder = HttpClients.custom().setConnectionManager(connectionManager).setRetryHandler(requestRetryHandler);

        clientBuilder.setDefaultRequestConfig(requestConfig);
        return clientBuilder.build();
    }
}